#pragma once

#include <sys/epoll.h>
#include <iostream>
#include <set>
#include <unistd.h>  // for close()
#include <cerrno>    // for errno
#include <cstring>   // for strerror()


namespace EpollerModule
{
    const static int default_epoll_fd = -1;

    enum Error
    {
        EpollError = 10,
        WaitError
    };

    class Epoller
    {
    public:
        Epoller(): _epoll_fd(default_epoll_fd) {}
        ~Epoller()
        {
            if(_epoll_fd != default_epoll_fd)
            {
                close(_epoll_fd);
            }
            std::cout << "epoll fd closed successfully" << std::endl;
        }
    public:
        void init()
        {
            // deprecated formal parameter of epoll size
            int size = 1024;
            _epoll_fd = epoll_create(size);
            if(_epoll_fd == -1)
            {
                std::cout << "epoll create failed" << std::endl;
                exit(EpollError);
            }
            std::cout << "epoll fd created successfully" << std::endl;
        }

        void addEvent(int fd, uint32_t events)
        {
            _fd_list.insert(fd);

            struct epoll_event ev;
            ev.events = events;
            ev.data.fd = fd;  // for user, data.fd is used to store the file descriptor

            int ret = epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
            if(ret == -1)
            {
                std::cerr << "epoll_ctl add failed, error: " << strerror(errno) << std::endl;
            }
        }

        void delEvent(int fd)
        {
            _fd_list.erase(fd);

            struct epoll_event ev;
            int ret = epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, &ev);
            if(ret == -1)
            {
                std::cerr << "epoll_ctl del failed, error: " << strerror(errno) << std::endl;
            }
        }

        int wait(struct epoll_event* events, int max_events, int timeout_ms)
        {
            int n = ::epoll_wait(_epoll_fd, events, max_events, timeout_ms);
            return n;
        }

        void printFdList()
        {
            std::cout << "fd list : ";
            for(auto fd : _fd_list)
            {
                std::cout << fd << " ";
            }
            std::cout << std::endl;
        }
    private:
        int _epoll_fd;
        std::set<int> _fd_list;  // for debug print
    };
}
